package com.kexilo.core.common.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

/**
 * MyBatis-Plus性能优化配置
 * 提供SQL性能监控、分页优化、防护拦截等功能
 * 
 * @author Kexilo
 */
@Configuration
public class MybatisPlusPerformanceConfig {
    
    private static final Logger log = LoggerFactory.getLogger(MybatisPlusPerformanceConfig.class);
    
    /**
     * MyBatis-Plus拦截器配置
     * 包含分页、乐观锁、防护等拦截器
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // 分页插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        paginationInterceptor.setDbType(DbType.POSTGRE_SQL);
        paginationInterceptor.setOverflow(false); // 溢出总页数后是否进行处理
        paginationInterceptor.setMaxLimit(1000L); // 单页分页条数限制
        paginationInterceptor.setOptimizeJoin(true); // 优化JOIN查询
        interceptor.addInnerInterceptor(paginationInterceptor);
        
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        // 防止全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        
        // 非法SQL拦截器
        IllegalSQLInnerInterceptor illegalSQLInterceptor = new IllegalSQLInnerInterceptor();
        interceptor.addInnerInterceptor(illegalSQLInterceptor);
        
        // 性能监控拦截器单独注册
        // 注意：PerformanceMonitorInnerInterceptor 应该单独注册为Bean
        
        log.info("MyBatis-Plus性能优化拦截器配置完成");
        return interceptor;
    }
    
    /**
     * 性能监控拦截器
     * 监控SQL执行时间和性能问题
     */
    @Intercepts({
        @Signature(type = Executor.class, method = "query", 
                  args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", 
                  args = {MappedStatement.class, Object.class})
    })
    public static class PerformanceMonitorInnerInterceptor implements Interceptor {
        
        private static final Logger perfLog = LoggerFactory.getLogger("SQL_PERFORMANCE");
        
        // 慢查询阈值（毫秒）
        private static final long SLOW_QUERY_THRESHOLD = 500;
        // 超长查询阈值（毫秒）
        private static final long VERY_SLOW_QUERY_THRESHOLD = 2000;
        
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            long startTime = System.currentTimeMillis();
            
            try {
                Object result = invocation.proceed();
                long endTime = System.currentTimeMillis();
                long executionTime = endTime - startTime;
                
                // 记录执行时间
                recordExecutionTime(invocation, executionTime);
                
                return result;
            } catch (Exception e) {
                long endTime = System.currentTimeMillis();
                long executionTime = endTime - startTime;
                
                // 记录异常执行时间
                recordErrorExecutionTime(invocation, executionTime, e);
                throw e;
            }
        }
        
        /**
         * 记录SQL执行时间
         */
        private void recordExecutionTime(Invocation invocation, long executionTime) {
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            String sqlId = mappedStatement.getId();
            
            if (executionTime > VERY_SLOW_QUERY_THRESHOLD) {
                perfLog.error("超长查询检测 - SQL ID: {}, 执行时间: {}ms", sqlId, executionTime);
            } else if (executionTime > SLOW_QUERY_THRESHOLD) {
                perfLog.warn("慢查询检测 - SQL ID: {}, 执行时间: {}ms", sqlId, executionTime);
            } else {
                perfLog.debug("SQL执行 - SQL ID: {}, 执行时间: {}ms", sqlId, executionTime);
            }
        }
        
        /**
         * 记录异常SQL执行时间
         */
        private void recordErrorExecutionTime(Invocation invocation, long executionTime, Exception e) {
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            String sqlId = mappedStatement.getId();
            
            perfLog.error("SQL执行异常 - SQL ID: {}, 执行时间: {}ms, 异常: {}", 
                         sqlId, executionTime, e.getMessage());
        }
        
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
        
        @Override
        public void setProperties(Properties properties) {
            // 可以通过properties设置阈值等参数
        }
    }
    
    /**
     * SQL性能分析拦截器
     * 分析SQL执行计划和性能瓶颈
     */
    @Intercepts({
        @Signature(type = Executor.class, method = "query", 
                  args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public static class SQLAnalysisInterceptor implements Interceptor {
        
        private static final Logger analysisLog = LoggerFactory.getLogger("SQL_ANALYSIS");
        
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            Object parameter = invocation.getArgs()[1];
            RowBounds rowBounds = (RowBounds) invocation.getArgs()[2];
            
            // 分析分页查询
            analyzePaginationQuery(mappedStatement, rowBounds);
            
            // 分析参数
            analyzeParameters(mappedStatement, parameter);
            
            return invocation.proceed();
        }
        
        /**
         * 分析分页查询
         */
        private void analyzePaginationQuery(MappedStatement mappedStatement, RowBounds rowBounds) {
            if (rowBounds != null && rowBounds != RowBounds.DEFAULT) {
                int offset = rowBounds.getOffset();
                int limit = rowBounds.getLimit();
                
                // 检测深分页
                if (offset > 10000) {
                    analysisLog.warn("深分页检测 - SQL ID: {}, Offset: {}, Limit: {}", 
                                   mappedStatement.getId(), offset, limit);
                }
                
                // 检测大页面
                if (limit > 1000) {
                    analysisLog.warn("大页面检测 - SQL ID: {}, Limit: {}", 
                                   mappedStatement.getId(), limit);
                }
            }
        }
        
        /**
         * 分析查询参数
         */
        private void analyzeParameters(MappedStatement mappedStatement, Object parameter) {
            if (parameter instanceof java.util.Collection) {
                java.util.Collection<?> collection = (java.util.Collection<?>) parameter;
                
                // 检测大批量IN查询
                if (collection.size() > 1000) {
                    analysisLog.warn("大批量IN查询检测 - SQL ID: {}, 参数数量: {}", 
                                   mappedStatement.getId(), collection.size());
                }
            }
        }
        
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
        
        @Override
        public void setProperties(Properties properties) {
            // 可配置参数
        }
    }
    
    /**
     * SQL分析拦截器Bean
     */
    @Bean
    public SQLAnalysisInterceptor sqlAnalysisInterceptor() {
        return new SQLAnalysisInterceptor();
    }
}
